package org.liurb.springboot.starter.web.jwt.aspect;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.liurb.springboot.starter.web.constants.BackendPublicConstants;
import org.liurb.springboot.starter.web.exception.BackendException;
import org.liurb.springboot.starter.web.http.response.ResultEnum;
import org.liurb.springboot.starter.web.jwt.annotation.JwtUserAnnotation;
import org.liurb.springboot.starter.web.jwt.dto.JwtUser;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt用户切面
 *
 * 从request的header中获取jwt，并封装到Controller方法的JwtUser类
 *
 * @Author Liurb
 * @Date 2022/11/28
 */
@Aspect
@Component
@Slf4j
@Order(11)
public class JwtUserAspect {

    /**
     * 定义切入点，其切入点表达式有多种匹配方式,这里是指定JwtUserAnnotation注解
     */
    @Pointcut("@annotation(org.liurb.springboot.starter.web.jwt.annotation.JwtUserAnnotation))")
    public void pointcut() {
    }

    /**
     * 前置通知：
     * 1.进入方法前进行jwt判断过滤拦截
     * 2.解密jwt内容并提取里面的用户信息
     * 3.获取controller的JwtUser对象，并对其进行赋值
     *
     * @param joinPoint
     * @param JwtUserAnnotation
     */
    @Before("pointcut() && @annotation(JwtUserAnnotation)")
    public void doBefore(JoinPoint joinPoint, JwtUserAnnotation JwtUserAnnotation) {

        if (JwtUserAnnotation.required()) {

            //获取jwt用户信息
            JwtUser jwtUser = this.getJwtUser();

            //设置参数JwtUser
            this.setParamsJwtUser(joinPoint, jwtUser);

        }
    }

    /**
     * 控制器的JwtUser进行赋值
     *
     * @param joinPoint
     * @param jwtUser
     */
    private void setParamsJwtUser(JoinPoint joinPoint, JwtUser jwtUser) {
        Object[] args = joinPoint.getArgs();
        Object obj = Arrays.stream(args).filter(s -> s instanceof JwtUser).findFirst().get();
        if (obj != null) {
            BeanUtil.copyProperties(jwtUser, obj);
        }
    }

    /**
     * 获取jwt信息的用户
     *
     * @return
     */
    private JwtUser getJwtUser() {
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest httpServletRequest = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        //请求request-id
        String requestId = httpServletRequest.getHeader("backend-request-id");

        String authorization = httpServletRequest.getHeader("Authorization");
        log.info("{} 鉴权Authorization信息 : {}", requestId, authorization);
        if (StrUtil.isEmpty(authorization)) {
            throw new BackendException(ResultEnum.AUTH_ERROR.getCode(), ResultEnum.AUTH_ERROR.getMsg());
        }

        //jwt验证
        String[] tokens = authorization.split(" ");
        if (null == tokens || tokens.length != 2 || StrUtil.isBlank(tokens[1])) {
            throw new BackendException(ResultEnum.AUTH_ERROR.getCode(), ResultEnum.AUTH_ERROR.getMsg());
        }

        String token = tokens[1];

        Algorithm algorithm = Algorithm.HMAC256(BackendPublicConstants.AUTH_SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();

        //验证jwt
        DecodedJWT jwt = verifier.verify(token);

        Map<String, Object> userMap = this.convertClaimToJson(jwt);

        if (ObjectUtil.isEmpty(userMap)) {
            throw new BackendException(ResultEnum.AUTH_ERROR.getCode(), ResultEnum.AUTH_ERROR.getMsg());
        }

        log.info("{} 鉴权用户信息 : {}", requestId, userMap);

        JwtUser jwtUser = new JwtUser();
        //封装appid
        String appId = (String)userMap.get("appid");
        if (StrUtil.isBlank(appId)) {
            appId = (String)userMap.get("appId");
        }
        if (StrUtil.isBlank(appId)) {
            appId = (String)userMap.get("app_id");
        }
        jwtUser.setAppId(appId);
        //封装活动settingId
        String settingId = (String)userMap.get("settingId");
        if (StrUtil.isBlank(settingId)) {
            settingId = (String)userMap.get("settingid");
        }
        if (StrUtil.isBlank(settingId)) {
            settingId = (String)userMap.get("setting_id");
        }
        jwtUser.setSettingId(settingId);
        //封装用户open_id
        String openId = (String)userMap.get("realopenid");
        if (StrUtil.isBlank(openId)) {
            openId = (String)userMap.get("openid");
        }
        if (StrUtil.isBlank(openId)) {
            openId = (String)userMap.get("openId");
        }
        if (StrUtil.isBlank(openId)) {
            openId = (String)userMap.get("open_id");
        }
        jwtUser.setOpenId(openId);
        //封装用户昵称
        String nickname = (String)userMap.get("nickname");
        if (StrUtil.isBlank(nickname)) {
            nickname = (String)userMap.get("nickName");
        }
        if (StrUtil.isBlank(nickname)) {
            nickname = (String)userMap.get("nick_name");
        }
        jwtUser.setNickname(nickname);
        //封装用户头像
        String headImgUrl = (String)userMap.get("headimgurl");
        if (StrUtil.isBlank(headImgUrl)) {
            headImgUrl = (String)userMap.get("headImgUrl");
        }
        if (StrUtil.isBlank(headImgUrl)) {
            headImgUrl = (String)userMap.get("head_img_url");
        }
        if (StrUtil.isBlank(headImgUrl)) {
            headImgUrl = (String)userMap.get("avatar");
        }
        jwtUser.setHeadImgUrl(headImgUrl);
        //channel
        String channel = (String)userMap.get("channel");
        jwtUser.setChannel(channel);
        //手机号码
        String phone = (String)userMap.get("phone");
        jwtUser.setPhone(phone);

        return jwtUser;
    }

    /**
     * 将jwt内的Claim转换为json
     *
     * @param jwt
     * @return
     */
    private Map<String, Object> convertClaimToJson(DecodedJWT jwt) {

        Map<String, Claim> claimsMap = jwt.getClaims();
        if (claimsMap != null) {
            Map<String, Object> params = new HashMap<>();
            for (Map.Entry<String, Claim> entry : claimsMap.entrySet()) {
                String key = entry.getKey();
                if (key != null && !key.matches("aud|sub|iss|exp|iat")) {

                    if (entry.getValue().asString() != null) {
                        params.put(key, entry.getValue().asString());
                    } else if (entry.getValue().asInt() != null) {
                        params.put(key, entry.getValue().asInt());
                    } else if (entry.getValue().asDouble() != null) {
                        params.put(key, entry.getValue().asDouble());
                    }

                }
            }
            return params;
        }

        return null;
    }

}
